#include "SymbolsTable.h"

namespace LALG
{

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  SymbolsTable
 * Description:  Constrói uma tabela de símbolos
 *--------------------------------------------------------------------------------------
 */
SymbolsTable::SymbolsTable()
{
    root = new SymTable();
    level.push_front(root);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  ~SymbolsTable
 * Description:  Destrói a tabela de símbolos
 *--------------------------------------------------------------------------------------
 */
SymbolsTable::~SymbolsTable()
{
    deleteSymbols(root);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  deleteSymbols
 * Description:  Deleta uma tabela de símbolos
 *--------------------------------------------------------------------------------------
 */
void SymbolsTable::deleteSymbols(SymTable* symbols)
{
    for(auto it : *symbols)
    {
        SymbolPtr sym = it.second;
        if(sym->category == PROCEDURE)
            delete sym->value.proc.params;
    }

    delete symbols;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  thereIs
 * Description:  Verifica se existe uma declaração
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::exists(const string &name, Category category)
{
    SymTable* symbols = level.front();

    auto it = symbols->find(name);
    if(it == symbols->end())
        return false;
    else
        return (category == ALL || it->second->category == category);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addSymbol
 * Description:  Adiciona um símbolo
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::addSymbol(const string &name, Category category, SymbolValue &value)
{
    SymbolPtr symbol(new Symbol);
    symbol->name = name;
    symbol->category = category;
    symbol->value = value;

    if(!exists(name))
    {
       SymTable &symbols = *(level.front());
       symbols[name] = symbol;
       return true;
    }
    return false;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addProgram
 * Description:  Adiciona um programa
 *--------------------------------------------------------------------------------------
 */
void SymbolsTable::addProgram(const string &name)
{
    SymbolValue value;
    addSymbol(name, PROGRAM, value);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addConstant
 * Description:  Adiciona uma constante
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::addConstant(const string &name, Variable &var)
{
    SymbolValue value;
    value.var = var;
    return addSymbol(name, CONSTANT, value);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addVariable
 * Description:  Adiciona uma variável
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::addVariable(const string &name, Variable &var)
{
    SymbolValue value;
    value.var = var;
    return addSymbol(name, VARIABLE, value);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addProcedure
 * Description:  Adiciona um procedimento
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::addProcedure(const string &name, Procedure &proc)
{
    SymbolValue value;
    value.proc = proc;
    return addSymbol(name, PROCEDURE, value);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  addParameter
 * Description:  Adiciona um parâmetro
 *--------------------------------------------------------------------------------------
 */
bool SymbolsTable::addParameter(const string &name, Variable &var)
{
    SymbolValue value;
    value.var = var;
    return addSymbol(name, PARAMETER, value);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  pushLevel
 * Description:  Insere um novo nível
 *--------------------------------------------------------------------------------------
 */
void SymbolsTable::pushLevel()
{
    SymTable *symbols = new SymTable();
    level.push_front(symbols);
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  popLevel
 * Description:  Remove o nível mais ao topo
 *--------------------------------------------------------------------------------------
 */
void SymbolsTable::popLevel()
{
    deleteSymbols(level.front());
    level.pop_front();
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  getSymbol
 * Description:  Retorna o valor de um símbolo
 *--------------------------------------------------------------------------------------
 */
Symbol* SymbolsTable::getSymbol(const string &name)
{
    for(SymTable* symbols : level)
    {
        auto it = symbols->find(name);
        if(it != symbols->end())
            return it->second.get();
    }

    return NULL;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  getCategory
 * Description:  Retorna a categoria
 *--------------------------------------------------------------------------------------
 */
Category SymbolsTable::getCategory(const string &name)
{
    Symbol* sym = getSymbol(name);
    return (sym == NULL) ? ALL : sym->category;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  getVariable
 * Description:  Retorna o valor de uma variável
 *--------------------------------------------------------------------------------------
 */
Variable* SymbolsTable::getVariable(const string &name)
{
    Symbol* sym = getSymbol(name);
    if(sym == NULL)
        return NULL;
    return (sym->category == VARIABLE || sym->category == CONSTANT ||
            sym->category == PARAMETER) ? &(sym->value.var) : NULL;
}

/*
 *--------------------------------------------------------------------------------------
 *       Class:  SymbolsTable
 *      Method:  getProcedure
 * Description:  Retorna o valor de um procedimento
 *--------------------------------------------------------------------------------------
 */
Procedure* SymbolsTable::getProcedure(const string &name)
{
    Symbol* sym = getSymbol(name);
    if(sym == NULL)
        return NULL;
    return (sym->category == PROCEDURE) ? &(sym->value.proc) : NULL;
}

}
